import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

/*
 * Was not sure about the number codes for states, so went with a simple 0-10.
 * 0 - no mines around it (blank)
 * 1-8 - number of mines around it
 * 9 - mine
 * 0x80 - unknown bitmask
 */

public class Board extends JFrame implements ActionListener, MouseListener {

	// vars -------------------------------------------------------------------
		// grid
		int rows = 16;
		int cols = 16;
		int numMines = 40;
		
		GridLayout layout = new GridLayout(rows, cols);
		char [] gridState = new char [rows*cols];
		
		// game state
		boolean won = false;
		boolean lost = false;
		
		boolean firstMove = false;
		boolean myTurn = false;
		
		boolean [] flags = new boolean [rows*cols];
		
		// java graphics
		JPanel p = new JPanel();
		JButton [] buttons = new JButton[rows*cols];
	
	// end vars ===============================================================
	
	// game methods -----------------------------------------------------------
	
	public Board() {
		p.setLayout(layout);
		initInput();
		for(int i = 0;i < rows*cols;i++){
			p.add(buttons[i]);
		}
		this.add(p);
		this.setVisible(true);
	}
	
	public void initInput() {
		for(int i = 0;i < (rows*cols);i++) {
			buttons[i] = new JButton();
			buttons[i].setPreferredSize(new Dimension(50,50));
			buttons[i].addActionListener(this);
			buttons[i].addMouseListener(this);
			buttons[i].setName(Integer.toString(i));
		}
	}
	
	// create mine layout, but only if first move and clicked (safe_x and safe_y are where clicked)
	public void makeMines(int safe_x, int safe_y) {
		for(int i = 0;i < numMines;i++) {
			boolean inSafeZone = true;
			int x = (int) Math.floor(Math.random() * rows);
			int y = (int) Math.floor(Math.random() * cols);
			
			// first check if already mine 
			if((gridState[(rows*y) + x]&0x7F) != 9) {
				// check if in the safe zone - click and squares around it (basically ensure click was on blank)
				for(int j = safe_x-1;j <= safe_x+1;j++){
					for(int k = safe_y-1;k <= safe_y+1;k++){
						if(x != k && y != j){
							inSafeZone = false;
						}
					}
				}
				if(!inSafeZone)
					gridState[(rows*y) + x] = (char) (0x80|9);
				else i--;
			} else i--;
		}
		calcNumbers();
	}
	
	public void calcNumbers() {
		for(int x = 0; x < rows;x++) {
			for(int y = 0;y < cols;y++) {
				int surMines = 0;
				if((gridState[x*rows + y]&0x7F) != 9) { // if it's not a mine
					// check surrounding squares for mines
					for(int j = Math.min(x-1,0);j <= Math.max(x + 1, rows-1);j++){
						for(int k = Math.min(y - 1,0);k <= Math.max(y + 1, cols-1);k++){
							if((gridState[x*rows + y]&0x7F) == 9)
								surMines++;
						}
					}
					// set to number of mines around it and set unknown bitmask
					gridState[x*rows + y] = (char) (0x80|surMines);
				}
			}
		}
	}
		
	// start the turn only; have to wait on input now
	public void startTurn(char[] newState) {
		if(newState == null)
			firstMove = true;
		else gridState = newState;
		
		myTurn = true;
		
		updateGraphics();
	}
	
	// turn is finished, return the board state
	public void endTurn() {
		myTurn = false;
		if(firstMove) firstMove = false;
		
		updateGraphics();
		// send gridstate back to main/networking function/class
	}
	
	public boolean reveal(int location) {
		// TODO implement
		if((gridState[location]&0x80) == 0)
			return false;
		if(firstMove)
			makeMines(location/rows,location%rows);
		if((gridState[location]&0x7F) == 0) {
			gridState[location] = (char) (gridState[location]&0x7F);
			return revealall(location);
		}
		else {
			gridState[location] = (char) (gridState[location]&0x7F);
			flags[location] = false;
			buttons[location].setText(Integer.toString(gridState[location]));
			if(gridState[location] == 9){
				lost = true;
				buttons[location].setText("X");
			}
			buttons[location].setEnabled(false);
		}
		return true;
	}
	
	public boolean revealall(int location) {
		// TODO implement
		// if is unknown, cannot use middle click
		if((gridState[location]&0x80) == 0x80)
			return false;
		boolean success = false;
		int numFlags = 0;
		// check if proper amount of flags have been set

		for(int x = Math.min(location/rows-1,0);x <= Math.max(location/rows+1, rows-1);x++) {
			for(int y = Math.min(location%rows, 0);y <= Math.max(location%rows, cols-1); y++) {
				if(flags[x*rows + y])
					numFlags++;
			}
		}
		
		if((gridState[location]&0x7F) == numFlags) {
			for(int x = Math.min(location/rows-1,0);x <= Math.max(location/rows+1, rows-1);x++) {
				for(int y = Math.min(location%rows, 0);y <= Math.max(location%rows, cols-1); y++) {
					success = success | reveal(x*rows + y);
				}
			}
		}
		return success;
	}
	
	public void setFlag(int location) {
		if((gridState[location]&0x80) == 0x80){
			flags[location] = !flags[location];
			updateGraphics();
			buttons[location].setText("F");
		}
	}
	
	public void updateGraphics() {
		
	}
	
	// end game methods =======================================================
	
	// input methods ----------------------------------------------------------
	
	@Override
	public void mouseClicked(MouseEvent arg0) {
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		int loc = Integer.parseInt(arg0.getSource().getClass().getName());
		
		// left click
		if(SwingUtilities.isLeftMouseButton(arg0) && myTurn) {
			if(reveal(loc))
				endTurn();
		}
		// right click
		else if(SwingUtilities.isRightMouseButton(arg0)) {
			setFlag(loc);
		}
		// middle click
		else if(SwingUtilities.isMiddleMouseButton(arg0) && myTurn) {
			if(revealall(loc))
				endTurn();
		}
		
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	// end input methods ======================================================

}
